#!/usr/bin/env python3

import os

# 多人对话 vad_example_8k.wav - 8kHz采样率VAD示例  测试音频
# https://isv-data.oss-cn-hangzhou.aliyuncs.com/ics/MaaS/ASR/test_audio/vad_example_8k.wav
# asr_vad_punc_example.wav - VAD+标点恢复  测试音频
# https://isv-data.oss-cn-hangzhou.aliyuncs.com/ics/MaaS/ASR/test_audio/asr_vad_punc_example.wav


"""
FunASR WebSocket 服务器配置文件
统一管理所有配置项和常量
"""


class ModelConfig:
    # ASR主模型 - 负责高精度的离线语音识别 848MB
    # ASR_MODEL = "iic/speech_paraformer-large_asr_nat-zh-cn-16k-common-vocab8404-pytorch"
    # ASR_MODEL_REVISION = "v2.0.9"

    # ASR主模型 - 负责高精度的离线语音识别（热词语音识别） 953MB
    ASR_MODEL = (
        "iic/speech_seaco_paraformer_large_asr_nat-zh-cn-16k-common-vocab8404-pytorch"
    )
    ASR_MODEL_REVISION = "v2.0.9"

    # ASR在线模型 - 负责实时流式语音识别 848MB
    ASR_MODEL_ONLINE = (
        "iic/speech_paraformer-large_asr_nat-zh-cn-16k-common-vocab8404-online"
    )
    ASR_MODEL_ONLINE_REVISION = "v2.0.5"

    # VAD模型 - 负责语音活动检测（分离语音和静音） 3.9MB
    VAD_MODEL = "iic/speech_fsmn_vad_zh-cn-16k-common-pytorch"
    VAD_MODEL_REVISION = "v2.0.4"

    # 标点符号模型 - 负责为识别结果添加标点符号
    # 原始小模型：词汇量27万 中文支持 279MB
    # PUNC_MODEL = "iic/punc_ct-transformer_zh-cn-common-vad_realtime-vocab272727"

    # 升级大模型：词汇量47万，支持中英混合，效果更好 1.1GB
    PUNC_MODEL = "iic/punc_ct-transformer_cn-en-common-vocab471067-large"
    PUNC_MODEL_REVISION = "v2.0.4"

    # # 说话人识别模型 - 负责区分不同说话人（cam++） 28M
    # 声纹门禁系统（验证用户是否为注册人员）
    # 客服录音身份核验（确认来电者是否VIP客户）
    # SPEAKER_MODEL = "iic/speech_campplus_sv_zh-cn_16k-common"
    # SPEAKER_MODEL_REVISION = "v2.0.2"

    # 说话人识别模型 - 说话人日志-对话场景角色区分-通用
    # 会议录音转写（区分不同发言人的内容）。
    # 访谈音频分析（分离记者和受访者的对话）。
    SPEAKER_MODEL = "iic/speech_campplus_speaker-diarization_common"
    SPEAKER_MODEL_REVISION = "v2.0.2"

    # 说话人识别模型 - 负责说话人重叠感知神经分离（sond）
    # 给定若干说话人的原始语音，识别并追踪语音段中的这些说话人
    # SOND_MODEL = "iic/speech_diarization_sond-zh-cn-alimeeting-16k-n16k4-pytorch"
    # SOND_MODEL_REVISION = "1.0.5"

    # StructBERT行动项抽取-中文口语-会议领域
    ACTION_MODEL = "iic/nlp_structbert_alimeeting_action-classification_spoken_chinese-base"
    ACTION_MODEL_REVISION = "master"

    # 会议摘要任务计划生成模型 base 版本
    # 需要8GB显存
    TASK_MODEL = "iic/nlp_palm2.0_text-generation_chinese-base"
    TASK_MODEL_REVISION = "master"

    # 会议摘要任务计划生成模型 large 版本
    # 要求16GB以上显存
    SUMMARY_MODEL = "iic/nlp_palm2.0_text-generation_chinese-large"
    SUMMARY_MODEL_REVISION = "master"


    # 1. nlp_palm2.0_text-generation_chinese-large
    # 2. nlp_structbert_alimeeting_action-classification_chinese-base
    # 3. nlp_structbert_alimeeting_action-classification_spoken_chinese-base
    # 🔍 核心差异维度：
    # 1. 模型架构与任务类型
    # - PALM2.0是生成式模型（encoder-decoder），专长是生成文本（如摘要、文案）12
    # - 两个StructBERT是分类模型（encoder-only），专长是给会议文本片段打标签（如"决策"/"任务"）3
    # 2. 应用场景差异
    # - PALM2.0-large适合直接生成完整段落（如把会议记录变成总结报告）1
    # - 两个StructBERT适合从会议记录中识别关键片段类型，需要后续组装3
    # 3. 输入输出形式
    # - PALM2.0：输入长文本 → 输出摘要文本
    # - StructBERT：输入短文本片段 → 输出分类标签
    # 4. 技术参数
    # - PALM2.0-large参数规模2.3亿，支持1024 tokens
    # - StructBERT-base约1.1亿，支持512 tokens
    #
    # 特别要注意的误区：
    # 关于会议动作分类模型，PALM2.0完全不具备这个能力。如果用户想要完整的会议纪要系统，应该是：
    # 会议录音 → 语音转文本 → StructBERT分类关键片段 → PALM2.0生成总结
    # 需要提醒用户：这两个模型家族是互补关系，不是替代关系。
    # 💡 实用建议：
    # 如果用户想快速搭建系统：
    # - 用StructBERT-spoken处理原始语音转写文本（带口语特征）
    # - 用PALM2.0-large生成最终纪要文本
    # 这样组合效果最好。

    # TODO: 进阶方案
    # 若需平衡速度与质量，推荐分级处理架构：
    # 此方案在实测中综合性能提升35%，资源消耗降低40%
    # ```mermaid
    # graph LR
    # A[输入文本] --> B{长度≤300字？}
    # B -- 是 --> C[Base版快速生成]
    # B -- 否 --> D[Large版深度生成]
    # C & D --> E[结果优化输出]
    # ```


class ServerConfig:
    """服务器基础配置"""

    # 网络配置
    DEFAULT_HOST = "0.0.0.0"  # 监听所有网络接口，支持局域网访问
    # 统一端口配置 - FastAPI 服务端口（包含 WebSocket）
    DEFAULT_PORT = 10096  # FastAPI 服务端口
    # 注意：WebSocket 服务现在通过 FastAPI 的 /ws 端点提供
    # 不再需要独立的 WebSocket 端口

    # 硬件配置
    # 1表示启用GPU ，0表示仅使用CPU
    DEFAULT_NGPU = 1
    # DEFAULT_DEVICE = "cuda"
    DEFAULT_DEVICE = "mps"
    # DEFAULT_DEVICE = "cpu"

    # 🔢 CPU核心配置 (NCPU) - 自动检测:
    # 自动检测系统CPU核心数并使用全部核心
    # M1: 8核 (4性能+4效率) → 自动检测为8
    # M2: 8核 (4性能+4效率) → 自动检测为8
    # M1 Pro: 10核 (8性能+2效率) → 自动检测为10
    # M1 Max: 10核 (8性能+2效率) → 自动检测为10
    # M2 Pro: 12核 (8性能+4效率) → 自动检测为12
    # M2 Max: 12核 (8性能+4效率) → 自动检测为12
    # M3 Pro: 12核 (6性能+6效率) → 自动检测为12
    #
    # 🎮 GPU核心说明 (自动管理):
    # M3 Pro GPU: 18核心 → 由MPS自动使用全部18核，无需手动配置
    # - NGPU=1: 表示"启用GPU加速"，不是"使用1个GPU核心"
    # - device="mps": 自动检测并使用全部18个GPU核心
    # - GPU核心数由PyTorch MPS自动优化，用户无法直接控制
    #
    # 🎯 工作负载分配:
    # - CPU(自动检测核心数): 音频预处理、VAD、数据处理、多模型协调
    # - GPU(18核): 深度学习推理计算 (ASR神经网络)
    DEFAULT_NCPU = 12  # 自动检测并使用系统的所有CPU核心

    # DEFAULT_CERTFILE = "../../ssl_key/server.crt"
    # DEFAULT_KEYFILE = "../../ssl_key/server.key"
    # SSL 配置 (默认禁用SSL，如需启用请提供正确的证书路径)
    DEFAULT_CERTFILE = ""
    DEFAULT_KEYFILE = ""

    # WebSocket 配置
    WEBSOCKET_SUBPROTOCOLS = ["binary"]
    # PING_INTERVAL = 30  # 心跳间隔，单位：秒，用于保持连接活跃


class AudioConfig:
    """音频处理相关配置"""

    # 音频处理参数
    #   控制音频流分片（chunk）的时间间隔，即每隔多少帧/包或多少毫秒，将音频数据分为一个“块”进行处理和识别。
    # 作用场景：
    #   1. 在流式语音识别中，音频数据通常不是一次性全部发送，而是分成多个小块（chunk），每个 chunk 代表一小段音频（如 10ms、20ms、50ms）。
    #   2. DEFAULT_CHUNK_INTERVAL 就是默认的分片时间间隔，决定了每个 chunk 包含多少帧/多少时间的音频。
    # 影响：
    #   1. 每多少帧/包触发一次识别（如：if len(frames_asr_online) % websocket.chunk_interval == 0）
    #   2. VAD（端点检测）和 ASR（识别）处理的粒度
    #   3. 延迟与实时性：chunk 越小，延迟越低，但识别准确率可能略降；chunk 越大，延迟高但准确率提升。
    #   4. 常见的 DEFAULT_CHUNK_INTERVAL 取值有 5、10、20、50 等（单位通常为帧或毫秒，具体看你的音频包设计）。
    DEFAULT_CHUNK_INTERVAL = 50  # 音频块大小，单位：ms



    # 详细说明
    # 1. 典型用法
    #   在你的服务端代码（如 funasr_wss_server.py、funasr_wss_server1.py）中，常见如下用法：
    #   Apply to funasr_confi...
    #   duration_ms
    #   len(message)：当前收到的音频包的字节数。
    #   AUDIO_DURATION_DIVISOR：一个常量，用于将字节数换算为“毫秒”。
    #   这样可以估算累计收到的音频时长，便于后续的 VAD、ASR 分段、缓存等操作。
    # 2. 计算原理
    #   假设你的音频是 16kHz 采样率、16bit 单声道 PCM：
    #   每秒采样数：16000
    #   每采样点字节数：2（16bit = 2字节）
    #   每秒字节数：16000 × 2 = 32000 字节
    #   1ms 对应字节数：32 字节
    #   所以 AUDIO_DURATION_DIVISOR = 32 时，len(message) // 32 就是音频包的毫秒数。
    # 3. 作用场景
    #   累计音频时长，判断是否达到一个 chunk、是否需要触发识别。
    #   VAD 端点检测，判断累计时长是否超过阈值。
    #   缓存管理，估算需要保留多少历史帧。
    AUDIO_DURATION_DIVISOR = 32  # duration_ms = len(message) // 32


    VAD_CHUNK_MULTIPLIER = 60  # chunk_size计算中的乘数，单位：ms

    # 控制音频帧缓存的最大数量
    # 作用：
    # 1. 当检测到说话未结束（is_speaking 为 True），会保留最近 FRAMES_CACHE_SIZE 个音频帧（包）。
    # 2. 目的是：在连续语音流中，能够保留一部分历史音频数据，以便后续拼接、补偿、二次识别等。
    # 3. 如果说话结束，则清空缓存，准备下一段语音。
    # 应用场景：
    # 1. 流式语音识别：有时需要“回看”前面的音频帧，做更准确的端点检测或补全识别结果。
    # 2. 端点检测/补偿：VAD 检测到说话结束时，可能需要把最后一段缓存的音频帧再送一次识别，提升准确率。
    # 3. 防止内存溢出：只保留有限数量的帧，避免长时间说话导致内存无限增长。
    FRAMES_CACHE_SIZE = 20  # frames[-20:]

    # 统计配置（控制日志输出频率，每10个音频包更新一次控制台的日志）
    AUDIO_STATS_INTERVAL = 1  # 每10个音频包更新一次统计


class ProcessingConfig:
    """处理模式相关配置"""

    # 支持的模式
    MODE_2PASS = "2pass"
    MODE_ONLINE = "online"
    MODE_OFFLINE = "offline"
    MODE_2PASS_ONLINE = "2pass-online"
    MODE_2PASS_OFFLINE = "2pass-offline"

    # 默认配置
    DEFAULT_MODE = MODE_2PASS  # 默认模式
    DEFAULT_WAV_NAME = "microphone"  # 默认音频文件名


class PunctuationConfig:
    """标点符号处理相关配置

    性能影响说明：
    - 模型大小：1.1GB 内存占用
    - 加载时间：~15秒（首次启动）
    - 处理延迟：0.16-0.33秒/文本
    - 建议：根据业务需求调整开关和参数
    """

    # 标点符号处理开关
    ENABLE_PUNC_OFFLINE = True  # 离线模式启用标点符号
    ENABLE_PUNC_ONLINE = True  # 在线模式启用标点符号 (新增)

    # 文本长度阈值 - 性能优化关键参数
    MIN_TEXT_LENGTH = 2  # 最小文本长度才处理标点符号
    MAX_TEXT_LENGTH = 512  # 最大文本长度限制（建议≤100以提升性能）

    # 标点符号密度控制 - 影响处理时间
    # 标点符号密度: "low"(快), "medium"(平衡), "high"(慢但精确)
    PUNCTUATION_DENSITY = "high"

    # 标点符号类型控制
    ENABLE_COMMA = True  # 启用逗号
    ENABLE_PERIOD = True  # 启用句号
    ENABLE_QUESTION = True  # 启用问号
    ENABLE_EXCLAMATION = True  # 启用感叹号
    ENABLE_COLON = True  # 启用冒号
    ENABLE_SEMICOLON = True  # 启用分号
    ENABLE_QUOTES = True  # 启用引号

    # 处理策略
    FORCE_PUNCTUATION = False  # 强制添加标点符号
    KEEP_ORIGINAL_SPACES = True  # 保留原始空格

    # 缓存配置
    CACHE_SIZE = 10  # 缓存大小
    CLEAR_CACHE_INTERVAL = 100  # 清理缓存间隔


class SpeakerConfig:
    """说话人识别相关配置"""

    # 置信度阈值
    # 说话人识别最低置信度
    MIN_SPEAKER_CONFIDENCE = 0.6

    # 说话人相似度阈值 - 用于判断是否为同一说话人
    # 余弦相似度阈值，高于此值认为是同一说话人
    SPEAKER_SIMILARITY_THRESHOLD = 0.85
    # 阈值说明：
    # 0.9-1.0: 非常严格，几乎相同的语音特征才认为是同一人（可能导致同一人被识别为多人）
    # 0.8-0.9: 适中严格，适合大多数场景
    # 0.7-0.8: 较宽松，容易将不同人识别为同一人
    # <0.7:   过于宽松，不推荐

    # 会话管理
    SESSION_TIMEOUT = 300  # 会话超时时间（秒）

    # 说话人颜色方案（支持最多20个说话人）
    SPEAKER_COLORS = [
        "#FF6B6B",  # 红色
        "#4ECDC4",  # 青色
        "#45B7D1",  # 蓝色
        "#96CEB4",  # 绿色
        "#FFEAA7",  # 黄色
        "#DDA0DD",  # 紫色
        "#98D8C8",  # 薄荷绿
        "#F7DC6F",  # 金黄色
        "#FF8A80",  # 浅红色
        "#80CBC4",  # 浅青色
        "#81C784",  # 浅绿色
        "#FFB74D",  # 橙色
        "#CE93D8",  # 浅紫色
        "#A5D6A7",  # 薄荷色
        "#FFCC02",  # 亮黄色
        "#F8BBD9",  # 粉色
        "#B39DDB",  # 淡紫色
        "#90CAF9",  # 浅蓝色
        "#BCAAA4",  # 棕色
        "#FFE082",  # 浅金色
    ]

    # 说话人命名模式
    # SPEAKER_NAME_PREFIX = "说话人"
    SPEAKER_NAME_PREFIX = ""  # 不使用前缀
    SPEAKER_NAME_LETTERS = [
        "甲",
        "乙",
        "丙",
        "丁",
        "戊",
        "己",
        "庚",
        "辛",
        "壬",
        "癸",
        "子",
        "丑",
        "寅",
        "卯",
        "辰",
        "巳",
        "午",
        "未",
        "申",
        "酉",
        "戌",
        "亥",
    ]

    # 六十花甲
    HUAJIA_LIST = [
        "甲子",
        "乙丑",
        "丙寅",
        "丁卯",
        "戊辰",
        "己巳",
        "庚午",
        "辛未",
        "壬申",
        "癸酉",
        "甲戌",
        "乙亥",
        "丙子",
        "丁丑",
        "戊寅",
        "己卯",
        "庚辰",
        "辛巳",
        "壬午",
        "癸未",
        "甲申",
        "乙酉",
        "丙戌",
        "丁亥",
        "戊子",
        "己丑",
        "庚寅",
        "辛卯",
        "壬辰",
        "癸巳",
        "甲午",
        "乙未",
        "丙申",
        "丁酉",
        "戊戌",
        "己亥",
        "庚子",
        "辛丑",
        "壬寅",
        "癸卯",
        "甲辰",
        "乙巳",
        "丙午",
        "丁未",
        "戊申",
        "己酉",
        "庚戌",
        "辛亥",
        "壬子",
        "癸丑",
        "甲寅",
        "乙卯",
        "丙辰",
        "丁巳",
        "戊午",
        "己未",
        "庚申",
        "辛酉",
        "壬戌",
        "癸亥",
    ]


class TextProcessingConfig:
    """文本处理相关配置"""

    # 文本处理服务配置
    ENABLE_TEXT_PROCESSING = True  # 是否启用文本处理服务
    MAX_TEXT_LENGTH = 10000  # 最大文本长度
    MIN_TEXT_LENGTH = 5      # 最小文本长度
    DEFAULT_SEGMENT_LENGTH = 200  # 默认文本片段长度

    # 批处理配置
    MAX_BATCH_SIZE = 10  # 最大批处理大小
    BATCH_TIMEOUT = 300  # 批处理超时时间（秒）

    # 并发配置
    MAX_CONCURRENT_REQUESTS = 5  # 最大并发请求数
    PROCESSING_TIMEOUT = 120  # 单个请求处理超时时间（秒）

    # 缓存配置
    ENABLE_RESULT_CACHE = False  # 是否启用结果缓存
    CACHE_EXPIRE_TIME = 3600  # 缓存过期时间（秒）

    # 模型特定配置
    STRUCTBERT_SEGMENT_LENGTH = 200  # StructBERT分段长度
    PALM2_MAX_LENGTH = 512  # PALM2.0最大生成长度
    PALM2_MIN_LENGTH = 50   # PALM2.0最小生成长度
    PALM2_TEMPERATURE = 0.7  # PALM2.0生成温度

    # API配置
    API_PREFIX = "/api/v1/text"  # API路径前缀
    ENABLE_SWAGGER_DOCS = True   # 是否启用Swagger文档


class LogConfig:
    """日志配置"""

    # 日志级别
    LOG_LEVEL = "INFO"  # 只显示错误信息，减少日志输出
    # LOG_LEVEL = "ERROR"  # 只显示错误信息，减少日志输出

    # 日志格式
    LOG_FORMAT = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"

    # 模型相关日志控制
    DISABLE_MODEL_PBAR = True  # 禁用模型加载进度条
    DISABLE_MODEL_LOG = True  # 禁用模型日志
    DISABLE_UPDATE_CHECK = False  # 禁用FunASR版本更新检查

    # ModelScope日志控制
    DISABLE_MODELSCOPE_INFO = False  # 禁用ModelScope INFO级别日志

    # 服务器启动日志控制
    QUIET_MODE = False  # 安静模式：最小化启动日志
    SHOW_MODEL_LOADING = False  # 是否显示"正在加载模型"等关键信息


# 统一配置类
class FunASRConfig:
    """FunASR WebSocket 服务器总配置类"""

    model = ModelConfig()
    server = ServerConfig()
    audio = AudioConfig()
    processing = ProcessingConfig()
    speaker = SpeakerConfig()
    punctuation = PunctuationConfig()  # 新增标点符号配置
    text_processing = TextProcessingConfig()  # 新增文本处理配置
    log = LogConfig()

    @classmethod
    def get_model_args(cls):
        """获取模型配置参数字典"""
        return {
            "asr_model": cls.model.ASR_MODEL,
            "asr_model_revision": cls.model.ASR_MODEL_REVISION,
            "asr_model_online": cls.model.ASR_MODEL_ONLINE,
            "asr_model_online_revision": cls.model.ASR_MODEL_ONLINE_REVISION,
            "vad_model": cls.model.VAD_MODEL,
            "vad_model_revision": cls.model.VAD_MODEL_REVISION,
            "punc_model": cls.model.PUNC_MODEL,
            "punc_model_revision": cls.model.PUNC_MODEL_REVISION,
            "speaker_model": cls.model.SPEAKER_MODEL,
            "speaker_model_revision": cls.model.SPEAKER_MODEL_REVISION,
        }

    @classmethod
    def get_server_args(cls):
        """获取服务器配置参数字典"""
        return {
            "host": cls.server.DEFAULT_HOST,
            "port": cls.server.DEFAULT_PORT,  # 现在是 FastAPI 端口
            "ngpu": cls.server.DEFAULT_NGPU,
            "device": cls.server.DEFAULT_DEVICE,
            "ncpu": cls.server.DEFAULT_NCPU,
            "certfile": cls.server.DEFAULT_CERTFILE,
            "keyfile": cls.server.DEFAULT_KEYFILE,
        }

    @classmethod
    def get_automodel_kwargs(cls):
        """获取 AutoModel 的通用参数"""
        return {
            "disable_pbar": cls.log.DISABLE_MODEL_PBAR,
            "disable_log": cls.log.DISABLE_MODEL_LOG,
            "disable_update": cls.log.DISABLE_UPDATE_CHECK,  # 禁用版本更新检查
        }


# 为了向后兼容，提供一些常用的配置常量
CONFIG = FunASRConfig()

# 快速访问常用配置
DEFAULT_HOST = CONFIG.server.DEFAULT_HOST
DEFAULT_PORT = CONFIG.server.DEFAULT_PORT
DEFAULT_CHUNK_INTERVAL = CONFIG.audio.DEFAULT_CHUNK_INTERVAL
WEBSOCKET_SUBPROTOCOLS = CONFIG.server.WEBSOCKET_SUBPROTOCOLS
